package game.waypoint;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import tools.Help;
import tools.Point;
public class WaypointSystem implements Serializable {
	public Vector<Waypoint> points;
	private int idc;
	public WaypointSystem() {
		points = new Vector<Waypoint>();
		idc = 0;
	}
	public void addWaypoint(Point p, boolean onFloor) {
		Waypoint wp = new Waypoint(p, idc, onFloor);
		points.add(wp);
		idc++;
	}
	public void connect(Waypoint w0, Waypoint w1, float dist) {
		w0.addNeighbor(w1, dist);
		w1.addNeighbor(w0, dist);
	}
	public void draw() {
		for (Waypoint p : points) {
			p.draw();
		}
	}
	public Waypoint getWaypointAt(Point mp, float mindist) {
		for (Waypoint p : points) {
			if (p.pos.distance(mp) < mindist) {
				return p;
			}
		}
		return null;
	}
	public void connectAuto(float mdist) {
		ArrayList<Waypoint> graph = new ArrayList<Waypoint>();
		for (Waypoint p : points) {
			graph.add(p);
		}
		for (int i = 0; i < graph.size() - 1; i++) {
			for (int j = i + 1; j < graph.size(); j++) {
				float dist = graph.get(i).pos.distance(graph.get(j).pos);
				if (dist < mdist) {
					connect(graph.get(i), graph.get(j), dist);
				}
			}
		}
	}
	public ArrayList<Waypoint> getPath(Waypoint start, Waypoint end, boolean onFloor) {
		ArrayList<Waypoint> path = new ArrayList<Waypoint>();
		for (Waypoint p : points) {
			p.cdist = Float.POSITIVE_INFINITY;
			p.cpre = null;
			if (p.onFloor == onFloor)
				p.cvisited = false;
			else
				p.cvisited = true;
		}
		start.cdist = 0;
		Waypoint c, u = start;
		// Dijkstra shortest path finding
		while (u != null) {
			float a = Float.POSITIVE_INFINITY;
			for (Waypoint p : points) {
				if (!p.cvisited && p.cdist < a) {
					u = p;
					a = p.cdist;
				}
			}
			if (u.cvisited) {
				break;
			} // no more waypoints can be reached->stop algorithm
			if (u != null) {
				for (int j = 0; j < u.neighbors.size(); j++) {
					Waypoint v = u.neighbors.get(j);
					if (!v.cvisited) {
						float alt = u.cdist + u.distances.get(j);
						if (alt < u.neighbors.get(j).cdist) {
							v.cdist = alt;
							v.cpre = u;
						}
					}
				}
				u.cvisited = true;
				if (u.id == end.id) {
					break;
				}
			}
		}
		c = end;
		path.add(c);
		while (c.cpre != null) {
			path.add(0, c.cpre);
			c = c.cpre;
		}
		if (path.get(0) != start) {
			path.clear();
		}
		if (path.size() == 0) {
			// Help.p("no path found from:"+start.id+" to "+end.id+" !");
			return null;
		}
		/*
		 * ArrayList<Waypoint> rpath = new ArrayList<Waypoint>(); while(path.size()>0){ rpath.add(path.get(path.size()-1)); path.remove(path.size()-1); }
		 */
		return path;
	}
	public void clear() {
		points.clear();
	}
	public String getFileString() {
		String s = "";
		for (Waypoint p : points) {
			s = s + p.getFileString() + "/";
		}
		return s;
	}
	public void fromFileString(String s) {
		String l[] = s.split("/");
		for (String p : l) {
			String pp[] = p.split(";");
			if (pp.length > 2) {
				Waypoint wp = new Waypoint(Point.valueOf(pp[1]), Integer.valueOf(pp[0]), Boolean.valueOf(pp[2]));
				points.add(wp);
				if (wp.id > idc)
					idc = wp.id + 1;
			}
		}
		for (String p : l) {
			try {
				String pp[] = p.split(";");
				int cid = Integer.valueOf(pp[0]);
				if (pp.length > 3) {
					for (int i = 3; i < pp.length; i++) {
						int oid = Integer.valueOf(pp[i]);
						connect(cid, oid);
					}
				}
			} catch (Exception e) {
			}
		}
	}
	public void remWaypoint(Waypoint wp) {
		for (Waypoint w : points) {
			if (w.id != wp.id) {
				w.removeNeighbor(wp);
			}
		}
		points.remove(wp);
	}
	private void connect(int cid, int oid) {
		Waypoint c = null, o = null;
		for (Waypoint p : points) {
			if (p.id == cid)
				c = p;
			else if (p.id == oid)
				o = p;
		}
		if (c != null && o != null) {
			connect(c, o, c.pos.distance(o.pos));
		}
	}
	public Vector<Waypoint> getWaypoints() {
		return this.points;
	}
	public Waypoint getNearest(Point from, boolean onFloor) {
		float md = Float.POSITIVE_INFINITY;
		Waypoint rw = null;
		for (Waypoint w : this.points) {
			if (w.onFloor == onFloor) {
				float dsq = w.pos.squaredDistance(from);
				if (dsq < md) {
					rw = w;
					md = dsq;
				}
			}
		}
		return rw;
	}
}
